<?php
// +----------------------------------------------------------------------
// | sfadmin - Suifeng Administration
// +----------------------------------------------------------------------
// | Copyright (c) 2017-2020 http://suifeng.tech All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: suifeng <1787585212@qq.com>
// +----------------------------------------------------------------------

namespace suifeng\admin;

class AdminMenu
{
    /**
     * 菜单数组。
     *
     * @var array
     */
    private $menu_array;

    public function __construct()
    {
        $this->menu_array = cache('suifeng_admin_menu::menu_array');

        if (!$this->menu_array || APP_DEBUG) {
            $menu_tree = new MenuItem(MenuItem::ROOT_NAME);
            model('admin/module')->invoke_all_modules('admin_menu', $menu_tree);

            //$menu_tree->sort();

            // 为顶层模块菜单项目都有一个_url属性，这样做的目的是点击顶部的模块菜单，能够有页面响应。
            // 如果有设置URL，则_url=url，否则_url=_findAvailbleUrl()。
            foreach ($menu_tree->children() as $module) {
                if (empty($module['default'])) {
                    $module['_url'] = $this->_findAvailbleUrl($module);
                } else {
                    $module['_url'] = $module['default'];
                }
            }

            // 缓存数组，没有办法缓存对象。
            $this->menu_array = $menu_tree->toArray();

            cache('suifeng_admin_menu::menu_array', $this->menu_array, 3600);
        }

        // 激活当前请求对应的菜单项
        $this->_set_active_url();
    }

    /**
     * 返回菜单数组。
     *
     * @return array
     */
    public function get_menu_array()
    {
        return $this->menu_array;
    }

    /**
     * 获取模块对应的菜单。
     *
     * @param unknown $module
     * @return unknown|NULL
     */
    public function get_module_menu($module = null)
    {
        if (empty($module)) {
            $module = request()->module();
        }

        foreach ($this->menu_array as $item) {
            if (isset($item['_name']) && strtolower($item['_name']) == strtolower($module)) {
                return $item;
            }
        }

        // 如果找不到模块菜单，返回admin。
        if (strtolower($module) !== 'admin') {
            return self::get_module_menu('admin');
        }

        return null;
    }









//     /**
//      * 返回后台管理菜单。
//      */
//     public function get_admin_menu_array()
//     {
//         $admin_menu = cache('admin_menu_service::admin_menu');

//         if (!$admin_menu || APP_DEBUG) {
//             $menu_tree = new MenuItem(MenuItem::ROOT_NAME);
//             model('admin/module')->invoke_all_modules('admin_menu', $menu_tree);

//             //$menu_tree->sort();

//             // 为顶层模块菜单项目都有一个_url属性，这样做的目的是点击顶部的模块菜单，能够有页面响应。
//             // 如果有设置URL，则_url=url，否则_url=_findAvailbleUrl()。
//             foreach ($menu_tree->children() as $module) {
//                 if (empty($module['default'])) {
//                     $module['_url'] = $this->_findAvailbleUrl($module);
//                 } else {
//                     $module['_url'] = $module['default'];
//                 }
//             }

//             // 缓存数组，不要直接缓存对象。
//             $admin_menu = $menu_tree->toArray();
//             cache('admin_menu_service::admin_menu', $admin_menu, 3600);
//         }

//         return $admin_menu;
//     }

    protected function _findAvailbleUrl($item)
    {
        if (isset($item['url']) && $item['url']) {
            return $item['url'];
        }

        foreach ($item->children() as $child) {
            $found = $this->_findAvailbleUrl($child);
            if ($found !== null) {
                return $found;
            }
        }

        return null;
    }

    /**
     * 设置菜单激活项目。
     *
     * @param unknown $array
     * @param unknown $url
     */
    protected function _set_active_url($url = null)
    {
        if ($url) {
            if (($found = $this->_search_item($this->menu_array, $url, true)) !== null) {
                return $found;
            }
        } else {
            // 查找module/controller/action
            $url = request()->module() . '/' .  request()->controller() . '/' . request()->action();
            if (($found = $this->_search_item($this->menu_array, $url, true)) !== null) {
                return $found;
            }

            // 查找module/controller/index
            $url = request()->module() . '/' .  request()->controller() . '/index';
            if (($found = $this->_search_item($this->menu_array, $url, true)) !== null) {
                return $found;
            }
        }
        return null;
    }

    /**
     * 通过URL查找菜单。
     *
     * @param unknown $array
     * @param unknown $url
     * @return NULL|boolean|NULL
     */
    public function find_by_Url($url)
    {
        if (($found = $this->_search_item($this->menu_array, $url, false)) !== null) {
            return $found;
        }
        return null;
    }

    /**
     * 返回指定字段列表。
     *
     * @param unknown $array
     * @param unknown $url
     * @param unknown $fieldName
     * @return array|NULL
     */
    public function field_list($url, $fieldName)
    {
        $fieldList = array();
        if ($this->_search_item($this->menu_array, $url, false, $fieldName, $fieldList) !== null) {
            return $fieldList;
        }
        return null;
    }

    protected function _search_item(&$array, $url, $active = false, $fieldName = null, &$fieldList = null)
    {
        foreach ($array as &$item) {
            if (isset($item['url']) && strtolower($item['url']) == strtolower($url)) {
                $active && $item['_active'] = true;
                $fieldName && array_unshift($fieldList, isset($item[$fieldName]) ? $item[$fieldName] : null);
                return $item;
            }

            if (($found = $this->_search_item($item['_children'], $url, $active, $fieldName, $fieldList)) !== null) {
                $active && $item['_active'] = true;
                $fieldName && array_unshift($fieldList, isset($item[$fieldName]) ? $item[$fieldName] : null);
                return $found;
            }
        }
        return null;
    }
}

class MenuItem implements \ArrayAccess
{
    /**
     * 根菜单标志。
     */
    const ROOT_NAME = '__ROOT__';

    /**
     * 上级。
     *
     * @var unknown
     */
    protected $_parent;

    /**
     * 名称。
     *
     * @var unknown
     */
    protected $_name;

    /**
     * 数据。
     *
     * @var unknown
     */
    protected $_data = array();

    /**
     * 子项目。
     *
     * @var array
     */
    protected $_children = array();

    /**
     * 构造方法。
     *
     * @param unknown $name
     * @param array $data
     */
    public function __construct($name, $data = array())
    {
        $this->_name = $name;
        $this->_data = $data;
    }

    /**
     * 检查制定key是否存在？
     *
     * @param  [type] $key [description]
     * @return [type]      [description]
     */
    public function offsetExists($offset)
    {
        return isset($this->_data[$offset]);
    }

    /**
     * 设置指定key的值。
     *
     * @param  [type] $offset [description]
     * @param  [type] $value  [description]
     * @return [type]         [description]
     */
    public function offsetSet($offset, $value)
    {
        $this->_data[$offset] = $value;
    }

    /**
     * 获取指定key的值。
     *
     * @param  [type] $offset [description]
     * @return [type]         [description]
     */
    public function offsetGet($offset)
    {
        return $this->_data[$offset];
    }

    /**
     * 删除指定key。
     *
     * @param  [type] $offset [description]
     * @return [type]         [description]
     */
    public function offsetUnset($offset)
    {
        unset($this->_data[$offset]);
    }

    /**
     * 获取ROOT项目。
     *
     * @return unknown
     */
    public function root()
    {
        $parent = $this;
        while ($parent->_parent !== null) {
            $parent = $parent->_parent;
        }
        return $parent;
    }

    /**
     * 获取TOP项目。
     *
     * @return unknown
     */
    public function top()
    {
        $parent = $this;
        while ($parent->_parent != null && $parent->_parent->_name <> self::ROOT_NAME) {
            $parent = $parent->_parent;
        }
        return $parent;
    }

    /**
     * 获取上级项目。
     *
     * @return [type] [description]
     */
    public function parent()
    {
        return $this->_parent;
    }

    /**
     * 获取项目路径。
     *
     * @return [type] [description]
     */
    public function path()
    {
        $list = array();

        $parent = $this;
        while ($parent->_parent !== null) {
            array_unshift($list, $parent->_name);
            $parent = $parent->_parent;
        }

        return implode('/', $list);
    }

    public function fieldList($field)
    {
        $list = array();

        $parent = $this;
        while ($parent->_parent !== null) {
            array_unshift($list, isset($parent[$field]) ? $parent[$field] : null);
            $parent = $parent->_parent;
        }

        return $list;
    }

    /**
     * 获取项目名称。
     *
     * @return [type] [description]
     */
    public function name()
    {
        return $this->_name;
    }

    /**
     * 获取项目值。
     *
     * @return [type] [description]
     */
    public function data()
    {
        return $this->_data;
    }

    /**
     * 获取子项目。
     *
     * @return [type] [description]
     */
    public function children()
    {
        return array_values($this->_children);
    }

    /**
     * 设置指定路径子项目的值。
     *
     * @param [type] $path [description]
     * @param [type] $data [description]
     */
    public function set($path, $data)
    {
        $child = $this->find($path, true);
        $child->_data = (array) $data;
        return $this;
    }

    /**
     * 获取指定路径子项目的值。
     *
     * @param  [type] $path [description]
     * @return [type]       [description]
     */
    public function get($path)
    {
        $child = $this->find($path, false);
        if ($child !== null) {
            return $child->_data;
        } else {
            return null;
        }
    }

    /**
     * 删除指定路径子项目。
     *
     * @param  [type] $path [description]
     * @return [type]       [description]
     */
    public function delete($path)
    {
        $path = trim($path, '/');
        $items = explode('/', $path);
        $last = array_pop(($items));

        if (count($items) > 0) {
            $parent = $this->find(implode('/', $items));
        } else {
            $parent = $this;
        }

        if (isset($this->_children[$last])) {
            unset($this->_children[$last]);
        }
    }

    /**
     * 设置指定路径子项目的值。
     *
     * @param [type] $name [description]
     * @param [type] $data [description]
     */
    public function add($name, $data)
    {
        $child = new MenuItem($name, (array) $data);
        $child->_parent = $this;
        $this->_children[$name] = $child;
        return $child;
    }

    /**
     * 查找指定路径的子项目，路径可以用abc/xyz/rst这种方式表示。
     *
     * @param unknown $path
     * @return NULL|\suifeng\mvc\TreeItem|mixed
     */
    public function find($path, $create = false)
    {
        $path = trim($path, '/');

        $parent =& $this;

        foreach (explode('/', $path) as $child_path) {
            if (isset($parent->_children[$child_path])) {
                $parent =& $parent->_children[$child_path];
            } else {
                //不存在
                if ($create) {
                    $parent =& $parent->add($child_path, []);
                } else {
                    return null;
                }
            }
        }

        return $parent;
    }

    /**
     * 排序菜单项目。
     *
     * @param unknown $key
     * @param unknown $order
     */
    public function sort()
    {
        $temp = array();

        foreach ($this->_children as $name => $child) {
            $temp[$name] = isset($child['sort']) ? $child['sort'] : 100;
        }

        asort($temp);

        foreach (array_keys($temp) as $name) {
            $temp[$name] = $this->_children[$name];
        }

        $this->_children = $temp;

        /*
        uasort($this->_children, function($a, $b) {
            $as = isset($a['sort']) ? $a['sort'] : 100;
            $bs = isset($b['sort']) ? $b['sort'] : 100;
            // 相等的情况下返回1，这样空着$a在前，$b在后。
            return $as == $bs ? 1 : ($as < $bs ? -1 : 1);
        });
        */

        foreach ($this->_children as $child) {
            $child->sort();
        }
    }

    public function toArray()
    {
        if ($this->_name == self::ROOT_NAME) {
            $list = array();
            foreach ($this->children() as $child) {
                $list[] = $child->toArray();
            }
            return $list;
        }

        $array = $this->data();
        $array['_name'] = $this->name();
        $array['_active'] = false;
        $array['_children'] = array();

        foreach ($this->children() as $child) {
            $array['_children'][] = $child->toArray();
        }

        return $array;
    }
}
